
PLUG.Platform = PLUG_PLATFORM_CLIENT

PLUG.ClassName = "cl_struct"
PLUG.PrintName = "Clientside Structure"
PLUG.Author = "thomasfn"

PLUG.Hard = true

PLUG.MenuItems = {}

PLUG.PRIVS = {}
PLUG.UGS = {}

PLUG.IsMenuOpen = false
PLUG.MenuLocked = false

function PLUG:AddHooks()
	concommand.Add( "+gmanager", function()
		plugs.Call( "OpenMenu" )
	end )
	concommand.Add( "-gmanager", function()
		if (self.MenuLocked) then return end
		plugs.Call( "CloseMenu" )
	end )
	plugs.Hook( PLUG_PLATFORM_CLIENT, "GetTeamColor" )
end

function PLUG:PlayerExtend( meta )
	function meta:UsergroupTable()
		return plugs.Call( "GetUG", self:GetNWString( "usr_group" ) )
	end
	function meta:Usergroup()
		local tab = self:UsergroupTable()
		if (tab) then
			return tab.PrintName
		else
			return self:GetNWString( "usr_group" )
		end
	end
end

function PLUG:ConCommand_CL( ply, com, args )
	if (com == "openmenu") then
		self:OpenMenu()
	end
	if (com == "closemenu") then
		self:CloseMenu()
	end
end

function PLUG:LockMenu()
	self.MenuLocked = true
end

function PLUG:AddMainMenuItem( classname, printname, image, priv )
	local tmp = {}
	tmp.ClassName = classname
	tmp.PrintName = printname
	tmp.Image = surface.GetTextureID( image )
	tmp.ImagePath = image
	tmp.Priv = priv
	table.insert( self.MenuItems, tmp )
end

function PLUG:GetMainMenuItems()
	return self.MenuItems
end

function PLUG:GetMainMenuItems_PRIV()
	local o = {}
	for _, v in pairs( plugs.Call( "GetMainMenuItems" ) or {} ) do
		if (plugs.Call( "HasPriv", v.Priv )) then
			table.insert( o, v )
		end
	end
	return o
end

function PLUG:DisableMenuOpen( b )
	self.MenuOpenDisallowed = b
end

function PLUG:MenuOpen()
	return self.IsMenuOpen
end

local bgcol = Color( 50, 50, 50, 180 )
function PLUG:OpenMenu()
	if (self.MenuOpenDisallowed) then return end
	if (!self:HasPriv( "viewmenu" )) then return end
	if (self.IsMenuOpen) then return end
	if (self.MenuObj && self.MenuObj:IsValid()) then return end
	local pn = vgui.Create( "GMAN_MMenu" )
	pn:Setup()
	self.MenuObj = pn
	gui.EnableScreenClicker( true )
	self.IsMenuOpen = true
end

function PLUG:CloseMenu()
	if (!self.IsMenuOpen) then return end
	if (self.MenuObj && self.MenuObj:IsValid()) then
		self.MenuObj:FadeOut()
		// self.MenuObj = nil
	end
	if (self.SubMenu && self.SubMenu:IsValid()) then
		self.SubMenu:Remove()
		self.SubMenu = nil
	end
	gui.EnableScreenClicker( false )
	self.IsMenuOpen = false
	self.MenuLocked = false
	self.MenuClosing = true
	self.NextOpen = CurTime() + 1
end

function PLUG:UserMessage( tag, um )
	if (tag == "message") then
		if (self.IsMenuOpen && self.MenuObj) then
			local msg = um:ReadString()
			self.MenuObj:TickerMessage( msg )
		end
		return true
	end
	if (tag == "mapchange") then
		local gm = um:ReadString()
		local map = um:ReadString()
		local time = um:ReadLong()
		plugs.Call( "CreateCountdown", { "[ Map Change ]", map, "@" .. tostring( time ) } )
	end
	if (tag == "cancelmapchange") then
		plugs.Call( "DestroyCountdown" )
	end
end

function PLUG:OpenSubMenu( classname )
	if (self.MenuClosing) && (CurTime() < self.NextOpen) then return end
	self.MenuClosing = false
	if (!self.IsMenuOpen) then return end
	if (self.SubMenu) then
		self.SubMenu:Remove()
		self.SubMenu = nil
	end
	self:LockMenu()
	local pn = vgui.Create( "DFrame", self.MenuObj )
	pn:SetPos( self.MenuObj:GetWide() * 0.02, self.MenuObj:GetTall() * 0.35 )
	pn:SetSize( self.MenuObj:GetWide() * 0.96, self.MenuObj:GetTall() * 0.63 )
	function pn:Paint()
		draw.RoundedBox( 6, 0, 0, self:GetWide(), self:GetTall(), bgcol )
	end
	pn:SetDraggable( false )
	pn:ShowCloseButton( true )
	pn:SetTitle( classname )
	pn._CLASSNAME = classname
	self.SubMenu = pn
	plugs.Call( "BuildSubMenu", classname, pn )
	pn:MakePopup()
	return pn
end

function PLUG:GetCurrentSubMenuClass()
	if (!self.SubMenu) then return end
	return self.SubMenu._CLASSNAME
end

function PLUG:BuildPlayerActionList( id )
	if (id == "BASIC") then
		return {
			{ "kick", "Kick", "kick" },
			{ "ban", "Ban", "ban" },
			{ "perma", "Perma Ban", "ban" },
			{ "setgrp", "Set Usergroup", "setgroup" },
			{ "givepriv", "Give privilege", "giveprivs" },
			{ "takepriv", "Take privilege", "giveprivs" },
			{ "givewep", "Give Weapon", "giveweps" }
		}
	end
	if (id == "PUNISH") then
		return {
			{ "slay", "Slay", "punish" }
		}
	end
end

function PLUG:RunPlayerOperation( op, ply, info )
	if ((!ply) || (!ply:IsValid())) then return end
	if (op == "kick") then
		self:OpenOpInfoMenu( plugs.MergeCall( "BuildKickReasonList" ), "dokick", ply )
	end
	if (op == "dokick") then
		if (info == "CUSTOM") then
			plugs.Call( "DoCustomKick", ply )
		else
			self:DoPlayerAction( ply, "kick", info )
		end
	end
	if (op == "ban") then
		self:OpenOpInfoMenu( plugs.MergeCall( "BuildBanTimeList" ), "doban", ply )
	end
	if (op == "doban") then
		self:DoPlayerAction( ply, "ban", info )
	end
	if (op == "perma") then
		local q = "Are you sure you want to perma ban that player?"
		plugs.Call( "PromptYesNo", q, function() plugs.Call( "RunPlayerOperation", "doperma", ply ) end )
	end
	if (op == "doperma") then
		self:DoPlayerAction( ply, "ban", 0 )
	end
	if (op == "setgrp") then
		self:OpenOpInfoMenu( self:GetUGSNames(), "dosetgrp", ply )
	end
	if (op == "dosetgrp") then
		self:DoPlayerAction( ply, "setgrp", info )
	end
	if (op == "slay") then
		self:DoPlayerAction( ply, "slay" )
	end
	if (op == "givepriv") then
		self:OpenOpInfoMenu( self:GetPrivOptions( self:GetPrivs( ply ) ), "dogivepriv", ply )
	end
	if (op == "takepriv") then
		self:OpenOpInfoMenu( self:GetPlayerPrivs( ply ), "dotakepriv", ply )
	end
	if (op == "dogivepriv") then
		self:DoPlayerAction( ply, "givepriv", info )
	end
	if (op == "dotakepriv") then
		self:DoPlayerAction( ply, "takepriv", info )
	end
	if (op == "givewep") then
		self:OpenOpInfoMenu( self:GetWeaponList(), "dogivewep", ply )
	end
	if (op == "dogivewep") then
		self:DoPlayerAction( ply, "givewep", info )
	end
end

function PLUG:SendCommand( ... )
	// print( "SENDING COMMAND:", ... )
	RunConsoleCommand( "gmanage_sv", ... )
	return true
end

function PLUG:DoPlayerAction( ply, action, ... )
	self:SendCommand( action, ply:GetGID(), ... )
end

function PLUG:OpenOpInfoMenu( oplist, op, ply )
	local m = DermaMenu()
	for _, v in pairs( oplist ) do
		local opname = v[1]
		local nice = v[2] or opname
		if (type( nice ) != "string") then
			ErrorNoHalt( "Caught a nasty table where there should have been a string!\n" )
			print( opname, nice )
		else
			m:AddOption( nice, function() plugs.Call( "RunPlayerOperation", op, ply, opname ) end )
		end
	end
	m:Open()
end

local kickreasons = {
	{ "Mingebag" },
	{ "Noob" },
	{ "Asshole" },
	{ "Spammer" },
	{ "Rulebreaker" },
	{ "Hacker" },
	{ "No Reason" },
	{ "Abused" },
	{ "Didn't listen to admin" },
	{ "Didn't stop when asked" },
	{ "Exploited a glitch" },
	{ "Aimbotting" },
	{ "CUSTOM" }
}
function PLUG:BuildKickReasonList()
	return kickreasons
end

local Second = 1
local Minute = Second * 60
local Hour = Minute * 60
local Day = Hour * 24
local Week = Day * 7
local Month = Week * 4

local bantimes = {
		{ 0, "Indefinite" },
		{ 5*Minute, "5 minutes" },
		{ 10*Minute, "10 minutes" },
		{ 30*Minute, "30 minutes" },
		{ 1*Hour, "1 hour" },
		{ 10*Hour, "10 hours" },
		{ 1*Day, "1 day" },
		{ 3*Day, "3 days" },
		{ 1*Week, "1 week" },
		{ 2*Week, "2 weeks" },
		{ 1*Month, "1 month" }
	}
function PLUG:BuildBanTimeList()
	return bantimes
end

//GetCountdownTimes
local cdowntimes = {
		{ 0, "Now" },
		{ 30, "30 seconds" },
		{ 1*60, "1 minute" },
		{ 2*60, "2 minutes" },
		{ 5*60, "5 minutes" },
		{ 30*60, "30 minutes" },
		{ 1*60*60, "1 hour" }
	}
function PLUG:GetCountdownTimes()
	return cdowntimes
end

local dict = {
	// all = "All",
	// none = "None",
	kick = "Kick Players",
	ban = "Ban Players",
	setgroup = "Set Player Usergroup",
	punish = "Punish Players",
	giveweps = "Give Weapons",
	// teleport = "Teleport Players",
	giveprivs = "Give/Take privileges",
	manage_players = "Manage Players",
	manage_groups = "Manage Groups",
	manage_server = "Manage Server",
	manage_limits = "Manage Limits",
	manage_restrict = "Manage Restrictions",
	viewmenu = "View Menu",
	private_chat = "Private Chat",
	noclip = "Use Noclip",
	spray = "Spray",
	creategroup = "Create Groups",
	editgroup = "Edit Groups",
	deletegroup = "Delete Groups",
	changelimits = "Change Limits",
	editserversettings = "Change Server Settings",
	changemap = "Change Map",
	physgun_players = "Physgun Players",
	physgun_all = "Physgun All Entities",
	toolgun_players = "Toolgun Players",
	toolgun_all = "Toolgun All Entities",
	status_admin = "Hold Admin Status",
	status_superadmin = "Hold Superadmin Status",
	rcon = "Remote Console"
}
function PLUG:GetPrivDictionary()
	for k, v in pairs( dict ) do
		if (type( v ) != "string") then
			print( "GetPrivDictionary()" )
			print( "CAUGHT A NASTY TABLE!", k, v )
			table.remove( dict, k )
		end
	end
	return dict
end

function PLUG:GetPrivOptions( omit )
	omit = omit or {}
	local op = plugs.MergeCallOverride( "GetPrivDictionary" )
	local tmp = {}
	for k, v in pairs( op ) do
		if (!table.HasValue( omit, k )) then table.insert( tmp, { k, v } ) end
	end
	// I know I know, it uses table.HasValue, but it's not in a paint hook or anything so nobody will know
	return tmp
end

function PLUG:GetPlayerPrivs( ply )
	local dict = plugs.MergeCall( "GetPrivDictionary" )
	local tmp = {}
	for _, v in pairs( self:GetPrivs( ply ) ) do
		table.insert( tmp, { v, dict[ v ] or v } )
	end
	return tmp
end

local weps = {
	{ "weapon_physcannon", 	"Gravity Gun" },
	{ "weapon_physgun", 	"Physics Gun" },
	{ "weapon_crowbar", 	"Crowbar" },
	{ "weapon_stunstick", 	"Stunstick" },
	{ "weapon_pistol", 		"Pistol" },
	{ "weapon_357", 		".357" },
	{ "weapon_smg1", 		"SMG" },
	{ "weapon_shotgun", 	"Shotgun" },
	{ "weapon_crossbow", 	"Crossbow" },
	{ "weapon_ar2", 		"AR2" },
	{ "weapon_bugbait", 	"Bug Bait" },
	{ "weapon_rpg", 		"RPG" }
}

function PLUG:GetWeaponList()
	return weps
end

function PLUG:GetPrivTitle( priv )
	return plugs.MergeCallOverride( "GetPrivDictionary" )[ priv ] or priv
end

function PLUG:DataStream( tbl )
	if (tbl[1] == "playerprivs") then
		self.PRIVS = self.PRIVS or {}
		if (!tbl[2]) then return end
		self.PRIVS[ tbl[2] ] = tbl[3]
		local ply = player.GetByGID( tbl[2] or 0 )
		if (ply && ply:IsValid()) then
			Msg( "Recieved player privs! (" .. ply:Nick() .. ")\n" )
		end
		// PrintTable( tbl[3] )
	end
	if (tbl[1] == "ugs") then
		self.UGS = tbl[2]
		Msg( "Recieved UGS!\n" )
		self.DefaultGroup = tbl[3] or ""
		// PrintTable( self.UGS )
	end
	if (tbl[1] == "ugdat") then
		self.UGS = self.UGS or {}
		self.UGS[ tbl[2] or "" ] = tbl[3]
	end
	if (tbl[1] == "gmdata") then
		self.Gamemodes = tbl[2]
		self.Maps = tbl[3]
		Msg( "Recieved GM Data!\n" )
	end
end

function PLUG:GetDefaultGroup()
	return self.DefaultGroup or ""
end

function PLUG:GetMapList()
	return self.Maps or {}
end

function PLUG:GetGamemodeList()
	return self.Gamemodes or {}
end

function PLUG:GetPrivs( ply )
	ply = ply or LocalPlayer()
	return self.PRIVS[ ply:GetGID() ] or {}
end

function PLUG:GetUGSNames()
	if (table.Count( self.UGS ) == 0) then return { { "nogroup", "No Usergroup Data" } } end
	local tmp = {}
	for _, v in pairs( self.UGS ) do
		table.insert( tmp, { v.ClassName, v.PrintName } )
	end
	return tmp
end

function PLUG:HasPriv( priv, ply )
	local ps = self:GetPrivs( ply )
	if (table.HasValue( ps, "none" )) then return end
	return table.HasValue( ps, "all" ) or table.HasValue( ps, priv )
end

function PLUG:GetUG( ugname )
	return self.UGS[ ugname ]
end

function PLUG:GetUGS()
	return self.UGS
end

function PLUG:GetTeamColor( ply )
	local grp = ply:UsergroupTable()
	// print( grp )
	if (grp && grp.Colour) then
		// print( grp.Colour )
		return grp.Colour
	end
end

local serveractions = {
	{ "general", "General Settings" },
	{ "gamemode", "Gamemode Actions" }
}
function PLUG:GetServerActions()
	return serveractions
end

local stoolsentsweps
function PLUG:GetStoolSentSweps()
	if (stoolsentsweps) then return stoolsentsweps end
	local tmp = {}
	local toolgun
	for _, v in pairs( weapons.GetList() ) do
		local ent = {}
		ent[1] = "swep"
		ent[2] = v.ClassName
		ent[3] = "[SWEP] " .. string.Replace( (v.PrintName or v.ClassName), "#", "" )
		table.insert( tmp, ent )
		if (v.ClassName == "gmod_tool") then toolgun = v end
	end
	for k, v in pairs( scripted_ents.GetList() ) do
		if (v && v.t && v.t.Spawnable) then
			local ent = {}
			ent[1] = "sent"
			ent[2] = k
			ent[3] = "[SENT] " .. string.Replace( (k or (v.t or {}).PrintName ), "#", "" )
			table.insert( tmp, ent )
		end
	end
	if (toolgun && toolgun.Tool) then
		for k, v in pairs( toolgun.Tool ) do
			local ent = {}
			ent[1] = "stool"
			ent[2] = k
			ent[3] = "[STOOL] " .. string.Replace( (v.Name or k), "#", "" )
			table.insert( tmp, ent )
		end
	end
	stoolsentsweps = tmp
	return tmp
end

function PLUG:GetSwepName( class )
	local swep = weapons.Get( class )
	if (swep) then return swep.PrintName or class end
	return class
end

function PLUG:GetSentName( class )
	local sent = scripted_ents.Get( class )
	if (sent) then return sent.PrintName or class end
	return class
end

function PLUG:ToHoursMins( secs )
	local mins = math.floor( secs/60 )
	local hours = math.floor( mins/60 )
	local leftmins = mins - (hours*60)
	return hours .. " hours, " .. leftmins .. " minutes"
end